/******************************************************************************
 * Copyright (C), 2014, BeeIoT Tech. Co., Ltd. 
 * FileName:        SMS.c
 * Author:          lvxin 
 * Version :        1.0
 * Date:            2014-12-29 16:02:42
 * Description:
 * Function List:   1. -------
 * History:
 * <author>     <time>                <version >  <desc> 
 * lvxin        2014-12-29 16:02:42   1.0         build this moudle 
 *****************************************************************************/

/******************************************************************************
 *  *INCLUDES
 *  */
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "sim900a_api.h"
#include "SMS.h"


#ifdef PLATFORM
#define     UPRINTF(...)     SIM900A_core_uprintf(__VA_ARGS__)
#else
#define     UPRINTF(...)    printf (__VA_ARGS__);
#endif

/******************************************************************************
 *  *MICROS
 *  */
#define     SM_BUFFER_SIZE  300

/******************************************************************************
 *  *TYPEDEF
 *  */

//定义_print_utf8_to_pdu()函数oper参数使用的枚举
typedef enum PDU_PRINT_OPER{
    PDU_PRINT       =   1 << 0,     //输出PDU字符串
    PDU_NOPRINT     =   1 << 1,     //不输出PDU字符串
    PDU_70WORDS_LIMIT = 1 << 2,     //70个字的限制
    PDU_67WORDS_LIMIT = 1 << 3,     //67个字的限制
    PDU_COUNT_PDU_LEN = 1 << 4,     //计算总共输出的PDU字符串长度
}pdu_print_oper_t;

/******************************************************************************
 *  *FUNCTION DECLARES
 *  */
static int _print_utf8_to_pdu(const char *src, size_t srcbytes, pdu_print_oper_t oper);
static void  _conv_normal_tel_to_PDU_tel(const char *src, int len, char *dest);//按PDU格式转换电话号

/******************************************************************************
 *  *LOCAL VARIABLES
 *  */
static char l_sm_buffer[SM_BUFFER_SIZE] = {0};
//存储收信人
static char l_sm_phone_number[20] = {0};

/******************************************************************************
 *  *PROFILE CALLBACKS(DECLARES)
 *  */
/*单条短信PDU*/
static char *l_pdu_msg_format[] = {
    /*[0]=*/"08", 
    //  +-------------------------------+------------------------------+
    //  | 短信中心短信中心号码长度（字  |    这里的 08 是固定          |
    //  | 节）包括第 2 段（1 字节）和   |    不用更改。                |
    //  | 第 3 段（7 字节）。           |                              |
    //  +-------------------------------+------------------------------+
    /*[1]=*/"91", 
    //  +-------------------------------+------------------------------+
    //  |号码类型，值 91 则是该号码定   |                              |
    //  | 义为国际号码、ISDN 或者电话   |  91 表示号码带国家编         |
    //  | 号码                          |  码，中国是 86。             |
    //  | Bit7 ：该位固定为 1；         |  这里的 91 是固定的          |
    //  | Bit6-4 ： 000- 未知，001- 国  |  ，不用更改。                |
    //  | 际号码，010- 国内号码，111-   |                              |
    //  | 备用；                        |                              |
    //  | Bit3-0 ： 0000- 未知，        |                              |
    //  | 0001-ISDN 号 码               |                              |
    //  | 或 者 电 话 号 码 ，          |                              |
    //  | 1111- 备用。                  |                              |
    //  | 91 二进制表示为 1001 0001     |                              |
    //  |                               |                              |
    //  |                               |                              |
    //  +-------------------------------+------------------------------+
    /*[2]=*/NULL,
    //  +-------------------------------+------------------------------+
    //  | 短信中心号码（经过调换后的），|  该段内容需要用户            |
    //  | 由下面的方法可得到该串数据    |  己根 据具 体 情况 改        |                  
    //  | 。                            |  变，短信中心每个城          |                  
    //  | 首先看“ 8613800775500 ”字     |  市不一样，同一个城          |                  
    //  | 符串的长度是否为偶数个，如果  |  市移动和联通的也不          |                  
    //  | 不是请在最后补 F ，然后把     |  一样。可以到网络查          |                  
    //  | “ 8613800775500F ”每两个进    |  找到该号码。这个号          |                  
    //  | 行对调得到。                  |  码是保存 在 SIM 卡          |                  
    //  | 86 对调成 68，13 对调成 31。  |  内的，如果不记得了，        |                  
    //  | 其它同样方法对调，最后就得到  |  可以填写，默认会使          |                  
    //  | 了数据：68 31 08 70 57 05 F0  |  用 SIM 卡内部的短           |                  
    //  |                               |  信中心号码。下面会          |
    //  |                               |  有介绍到如果不填写          |
    //  |                               |  短信中心号码如何发          |
    //  |                               |  送。                        |
    //  +-------------------------------+------------------------------+
    //
    /*[>[3]=<]"51",//用于长短信*/
    /*[>[3]=<]"31",*/
    /*[3]=*/NULL,
    //  +-------------------------------+------------------------------+
    //  |发 送 参 数 ： 基 本 参 数     |                              |
    //  |(TPMTI/VFP) 发 送 ， TP-VP     |  固定，不用改变。            |
    //  |用相对格式这个字段不要去改变   |                              |
    //  |它， 用11 即可。               |                              |
    //  | 31要求回复报告
    //  +-------------------------------+------------------------------+
    //
    /*[4]=*/"00",
    //  +-------------------------------+------------------------------+
    //  |消息基准值 (TP-MR)这个字段     | 固定，不用改变。             |
    //  |不要去改变它，用 00 即可。     |                              |
    //  +-------------------------------+------------------------------+
    //
    /*[5]=*/"0D",
    //  +-------------------------------+------------------------------+
    //  |目标手机号码(第 8 段,不包括    |                              |          
    //  |F)的长度(个数)，不包括 91，    |                              |
    //  |不包括补加的 F 。注意了，这    |   接收短信的手机号码         |
    //  |和前面第 1 段的 08 定义是不    |   的长度，如有国码，         |
    //  |一样的。这个 0D 指的是个数，   |   则包含国码的长度。         |
    //  |例 如 第 8 段 的 值 为         |                              |
    //  |“ 688176372163F2 ” ，这里      |                              |
    //  |包含有 7 个字节 ， 但是个数    |                              |
    //  |却是 13（十六进制 0x0D）。     |                              |
    //  +-------------------------------+------------------------------+
    //                                  
    /*[6]=*///"91",
    /*[6]=*/NULL,
    //  +-------------------------------+------------------------------+
    //  |目标号码格式，和第 2 段的 91   |  表示国家编码，中国          |                   
    //  |定义相同                       |  是 86，【 A1：国内格        |                  
    //  |                               |  式；91：国际格式；          |
    //  |                               |  81：未知】                  |
    //  +-------------------------------+------------------------------+
    //   
    /*[7]="8176372163F2",*/
    /*[7]=*/NULL,
    //
    //  +-------------------------------+------------------------------+
    //  |目标号码，不是偶数位补 F ，    |                              |
    //  |然后两两对调，和第 3 段的转    |  根据发送号码更改            |
    //  |换方法一样。                   |                              |
    //  +-------------------------------+------------------------------+
    //
    //
    /*[8]=*/"00",
    //  +-------------------------------+------------------------------+
    //  |协议标识，00 表示普通 GSM      |  固定，不用改变。            |
    //  |类型，点到点方式。             |                              | 
    //  +-------------------------------+------------------------------+
    //
    //   
    /*[9]=*/"08",
    //  +-------------------------------+------------------------------+
    //  |信息内容使用的编码：           |         ^                    |
    //  |00---7Bit 编码格式，默认字符   |                              |
    //  |集；                           |  固定，不用改变。            |
    //  |F6---8Bit 编码格式 Class1；    |                              |
    //  |08---UCS2 （ 16bit ） 编 码，  |                              |
    //  |(Unicode)格式本例使用 UCS2     |                              |
    //  |编。                           |                              |
    //  +-------------------------------+------------------------------+
    //  
    //   
    /*[10]=*/"00",
    //  +-------------------------------+------------------------------+
    //  |短信有效期：                   |                              | 
    //  |00---5 分钟                    |  保存在短信中心的有          | 
    //  |……                             |  效时间。数值越大有          | 
    //  |A7---24 小时                   |  效期越长。                  | 
    //  |……                             |                              |
    //  |FF---135 周。                  |                              | 
    //  +-------------------------------+------------------------------+
    //   
    //[11]="06",
    /*[11]=*/NULL,
    //  +--------------------------------------------------------------+
    //  |信息长度，即后面短信内容的字                                  |  
    //  |节数(每两个字符为一字节)。计                                  |  
    //  |算公式为：字数 *2 。（十六进                                  |  
    //  |制 0x0C ）。                                                  |  
    //  +--------------------------------------------------------------+
    //
    //   
    //[12]="4F60597DFF01"
    /*[12]=*/NULL
        //  +--------------------------------------------------------------+
        //  |需要发送短信内容的 Unicode编码值，每个字符占用两个字节        |                      
        //  |来存储。如本例中需要发送的内容为：你好！4F60 为 “你”的        |
        //  |Unicode 编码值、 597D 为“好”的 Unicode 编码值、 FF01 为“！”   |
        //  |的 Unicode 编码值。                                           |
        //  |要 把 字 符 转 成 Unicode 码请按照下面的方法：在 VB 中        |
        //  |可以使用函数 AscW( )来把字符或者中文转换成 Unicode 码         |
        //  |如:函数 AscW(“中”)返回的值为十六进制 4E2D ，注意，还          |
        //  |要 把 值 4E2D 转 成 字 符串“4E2D”，此时可以用 VB 函           |
        //  |数 Hex(值) 把值转成对应的十六进制表示的字符。                 |
        //  |示例代码：                                                    |
        //  |Dim val as long                                               |
        //  |Dim str as string                                             |
        //  |val = AscW("中")                                              |
        //  |str = Hex(val)                                                |
        //  |这时 str 中就包含了转换后得到的字符串"4E2D"                   |
        //  +--------------------------------------------------------------+
};

/******************************************************************************
 *  *FUNCTION DECLARES
 *  */
static int _SMS_vcprintf (const char *phone_num, const char *fmt, va_list va);

/******************************************************************************
 *  *LOCAL FUNCTIONS
 *  */
/******************************************************************************
 * Function:        _print_utf8_to_pdu()
 * Description:     转换utf8编码输出为pdu字符串
 * Calls:            
 * Called By:       SMS_send_utf8_SM()
 * Called By:       SMS_printf()
 * Input:           src --utf8字符串
 * Input:           srcbytes --utf8字符串长度
 * Input:           oper --操作参数
 * Return:          默认返回为处理过的utf8字符串长度
 * Return:          当操作参数为 PDU_COUNT_PDU_LEN时为输出的PDU字符串的总长度
 * Others:          none 
 *****************************************************************************/
static int _print_utf8_to_pdu(const char *src, size_t srcbytes, pdu_print_oper_t oper)
{   
    uint8_t mark;
    uint16_t uc = 0;
    int follow = 0;
    size_t inbytes = srcbytes;
    int pdu_len = 0;
    int proc_bytes = 0;
    const char *ibuf, *ibuf_end;
    if ((src&&inbytes)){
        ibuf = src;
        ibuf_end = src+inbytes;
        while(ibuf<ibuf_end){
            if (oper & PDU_70WORDS_LIMIT)
            {
                if (pdu_len >= 280)
                {
                    break;
                }
            }
            if (oper & PDU_67WORDS_LIMIT)
            {
                if (pdu_len >= 268)
                {
                    break;
                }
            }
            mark = (uint8_t)*ibuf++; 
            proc_bytes ++;
            if (mark<0xF0 && mark>0xE0){
                /* 1110XXXX */
                uc = mark&0x0F;
                follow = 2;
            }else if (mark<0xE0 && mark>0xC0){
                /* 110XXXXX */
                uc = mark&0x1F;
                follow = 1;
            }else if (mark<0x80){
                /* 0XXXXXXX */
                uc = mark;
                follow = 0;
            }else{
                /* 转换失败: 0xF0 0xE0 不应该出现在 UTF-8 中 */
                break;
            }
            if (&ibuf[follow] > ibuf_end){
                /* unexpect input end */
                return proc_bytes;
            }
            for (; follow>0; follow--){
                /* 10XX.XXXX 0x80-0xBF*/
                if ((*ibuf&0xC0) != 0x80){
                    inbytes = ibuf_end - src;
                    break;
                }
                uc = (uc<<6)|(*ibuf++&0x3F);
                proc_bytes ++;
            }
            if (oper & PDU_PRINT)
            {
                UPRINTF ("%04X",uc);
#if(PLATFORM == LINUX)
                printf ("%04X",uc);
#endif
            }
            pdu_len += 4;
        }
    }
    if (oper & PDU_COUNT_PDU_LEN)
    {
        return pdu_len;
    }
    return proc_bytes;
}



static int _SMS_vcprintf (const char *phone_number, const char *fmt, va_list va)
{
    int buff_len = 0;  //短信缓冲区中的短信长度
    int buff_remain = 0; //剩余缓冲区
    int str_len = 0; //转换完需要的字节数
    /*int proc_len = 0; //buff被处理过的字节数*/

    if (strlen(phone_number) == 0) return -1;

    buff_len = strlen (l_sm_buffer);
    buff_remain = SM_BUFFER_SIZE - buff_len;

    str_len = vsnprintf (l_sm_buffer + buff_len, 0, fmt, va);

    while (str_len >= buff_remain)
    {
        /*proc_len = _print_utf8_to_pdu (l_sm_buffer, strlen(l_sm_buffer), PDU_NOPRINT | PDU_70WORDS_LIMIT);*/
        DBG_PRINTF ("\n[缓冲区释放]\n");
        DBG_PRINTF ("[短信内容]：%s\n", l_sm_buffer);

        /*发送短信*/
        if ((SMS_send_utf8_SM (phone_number, l_sm_buffer, buff_len) == -1)
               && (SMS_send_utf8_SM (phone_number, l_sm_buffer, buff_len) == -1)
               && (SMS_send_utf8_SM (phone_number, l_sm_buffer, buff_len) == -1))
        {}

        memset (l_sm_buffer, 0, SM_BUFFER_SIZE);

        /*buff_len -= proc_len; //剩余数据长度 */
        /*memmove(l_sm_buffer, l_sm_buffer + proc_len, buff_len);*/
        /*memset (l_sm_buffer+buff_len, 0, SM_BUFFER_SIZE - buff_len);*/
        buff_remain = SM_BUFFER_SIZE;
        buff_len = 0;
    }

    vsnprintf(l_sm_buffer + buff_len, buff_remain, fmt, va);

    return str_len;
}


/*******************************************************************************
 * Function:      conv_normal_tel_to_PDU_tel()
 * Description:   按PDU格式转换电话号
 * Calls:         
 * Called By:     SIM900A_send_PDU_SM()
 * Input:         src   --原始电话号
 *                len   --号码长度
 *                dest  --保存到的地址
 * Return:        void
 * Others:        None
 ******************************************************************************/
static void _conv_normal_tel_to_PDU_tel(const char *src, int len, char *dest)
{
    int i;
    if (len % 2 == 1)
    {
        len --;
        *(dest + len) = 'F';
        *(dest + len + 1) = *(src + len);
    }
    for (i = 0; i < len; i += 2)
    {
        *(dest + i) = *(src + i + 1);
        *(dest + i + 1) = *(src + i);
    }
}

/******************************************************************************
 *  *PUBLIC FUNCTIONS
 *  */

int SMS_flush (void)
{
    int ret = -1;
    DBG_PRINTF ("[flush短信内容]：");
    DBG_PRINTF ("%s\n", l_sm_buffer);
    if (strlen(l_sm_buffer) == 0 || strlen(l_sm_phone_number) == 0) return -1; //空数据的时候不发送短信
    ret = SMS_send_utf8_SM (l_sm_phone_number, l_sm_buffer, strlen (l_sm_buffer));

    memset(l_sm_buffer, 0, SM_BUFFER_SIZE);
    return ret;
}


int SMS_cprintf(const char *phone_num, const char *fmt, ...)
{
    int ret = -1;
    va_list va;
    va_start(va, fmt);
    ret = _SMS_vcprintf (phone_num, fmt, va);
    DBG_PRINTF ("[flush短信内容]：");
    DBG_PRINTF ("%s\n", l_sm_buffer);
    if (strlen(l_sm_buffer) == 0 || strlen(phone_num) == 0) return -1; //空数据的时候不发送短信
    ret = SMS_send_utf8_SM (phone_num, l_sm_buffer, strlen (l_sm_buffer));
    memset(l_sm_buffer, 0, SM_BUFFER_SIZE);
    va_end(va);
    return ret;
}

/*int SMS_printf (char *fmt, ...)*/
int SMS_printf (const char *fmt, ...)
{
    int ret = -1;
    va_list va;
    va_start(va, fmt);
    ret = _SMS_vcprintf (l_sm_phone_number, fmt, va);
    va_end(va);
    return ret;
}

/*设置收信人*/
int SMS_connect(const char *num, int len)
{
    memset (l_sm_phone_number, 0, 20);
    strncpy(l_sm_phone_number, num, len);
    if (l_sm_phone_number[0] == '+')
    {
        /*strcpy (l_sm_phone_number, l_sm_phone_number+3);*/
        memmove(l_sm_phone_number,l_sm_phone_number+3, strlen (l_sm_phone_number+3)+1);
    }
    return 0;
}

int SMS_send_utf8_SM(const char *num, const char *msg, int msg_len)
{
    int i;
    char *csca_num;
    char csca_addr[20] = {0};//消息中心地址
    char dest_addr[20] = {0};//目标手机号
    char dest_addr_len[3] = {0}; //目标手机号码长度
    char msg_len_s[5] = {0}; //短信正文长度
    /*int ret = 0;*/
    int num_len = 0;

    int proc_total_len = 0; //处理的源数据总长度
    int proc_len = 0; //处理的源数据长度
    int pdu_len = 0;  //输出的PDU串长度
    int clen = 0;
    int sms_split_count = 1; //短信拆分条数
    int UDH_len = 0;        //用户数据头长度
    int msg_id = 0; //当前短信序号
    uint8_t RN; //短信唯一标识
    pdu_print_oper_t word_limit = PDU_70WORDS_LIMIT; //PDU字数限制标识
    /*uint8_t ran ; //长短信用的随机数*/

    csca_num= SIM900A_api_get_SMS_address() + 1;//消息中心
        
    RN = (uint8_t)rand();

#ifndef NOSIM_DEBUG
    /*{++++++++++++入口验证++++++++++++*/
    if (SIM900A_api_get_CREG() == 0) //网络未注册
    {
        return -1;
    }
    if (msg_len == 0 || msg == NULL)
    {
        return -1;
    }
    //消息中心号码不正确时返回错误
    if (strlen(csca_num) == 0)
    {
        SIM900A_api_init_SMS_address();
        csca_num= SIM900A_api_get_SMS_address() + 1;//消息中心
        if (strlen(csca_num) == 0) return -1;
    }
    /*}------------入口验证------------*/
#endif

    //计算总共生成PDU字符串所需字符总数
    pdu_len = _print_utf8_to_pdu (msg,msg_len,
            PDU_NOPRINT | PDU_COUNT_PDU_LEN);

    if (pdu_len > 280)
    {
        sms_split_count = pdu_len / 268 + ((pdu_len%268 == 0)?0:1); //每条限制在67个字
        word_limit = PDU_67WORDS_LIMIT;
    }

    if (sms_split_count == 1)
    {
        l_pdu_msg_format[3] = "31";//单条短信
        UDH_len = 0; 
    }
    else
    {
        l_pdu_msg_format[3] = "51";//长短信
        UDH_len = 6; 
    }

    DBG_PRINTF ("[SMS_send PDU_TOTAL]%d\n", pdu_len);
    DBG_PRINTF ("[SMS_send SMS_SPLIT]%d\n", sms_split_count);

    num_len = strlen (num); //手机号码长度
    num_len = num_len/2 + num_len%2; //换算长度

    do {
        msg_id ++;
        /*msglen = strlen(tmp.pos);*/
        clen = strlen(msg + proc_total_len);

        //计算最终输出的PDU字符串长度，给AT+CMGS指令使用
        pdu_len = _print_utf8_to_pdu (msg + proc_total_len, 
                (clen<msg_len?clen:msg_len), //在参数值和字符串长度之间取最小
                PDU_NOPRINT | word_limit | PDU_COUNT_PDU_LEN);

        DBG_PRINTF ("[PDU_LEN]%d\n", pdu_len);
        DBG_PRINTF ("[NUM_LEN]%d\n", strlen(num));


#ifndef NOSIM_DEBUG
        /*发送短信*/
        if (SIM900A_perform_formatted(20000, NULL, NULL,"AT+CMGS=%d\r\n",
                    (pdu_len/2 + 8 + UDH_len + num_len)) != AT_RETURN_CON)
        {
            return -1;
        }
#endif
        sprintf (dest_addr_len, "%02X", strlen (num)); //获得目标手机号码长度

        //停止串口接收
        SIM900A_core_disable_uart_recv();

        l_pdu_msg_format[5] = dest_addr_len;
        if (num[0] == '8' && num[1] == '6')
        {
            l_pdu_msg_format[6] = "91"; //国际格式
        }
        else
        {
            /*l_pdu_msg_format[6] = "86"; //国内格式*/
            l_pdu_msg_format[6] = "81"; //未知格式
        }

        _conv_normal_tel_to_PDU_tel (num, strlen (num), dest_addr); //交换字节序列
        _conv_normal_tel_to_PDU_tel (csca_num, strlen (csca_num), csca_addr);

        l_pdu_msg_format[7]  = dest_addr;  //目标手机号
        l_pdu_msg_format[2]  = csca_addr;  //消息中心地址
        l_pdu_msg_format[11] = msg_len_s; //短信正文长度
        sprintf (msg_len_s, "%02X", pdu_len/2 + UDH_len);

        for (i = 0; i < (sizeof(l_pdu_msg_format)/sizeof(l_pdu_msg_format[0])) - 1; i ++) //拼接字段
        {
            SIM900A_core_uprintf (l_pdu_msg_format[i]);
#ifdef  OUTPUT_SMS_PDU
            printf ("%s", l_pdu_msg_format[i]);
#endif
        }
        if (sms_split_count > 1)
        {
            SIM900A_core_uprintf ("050003%02X%02X%02X", RN, sms_split_count, msg_id);
#ifdef  OUTPUT_SMS_PDU
            printf ("050003%02X%02X%02X", RN, sms_split_count, msg_id);
#endif
        }

        //发送短信正文
        proc_len = _print_utf8_to_pdu (msg + proc_total_len, 
                (clen<msg_len?clen:msg_len),
                PDU_PRINT | word_limit);

        SIM900A_core_enable_uart_recv();
        SIM900A_core_clean_uart_rxbuf(); //清除缓冲区

#ifndef NOSIM_DEBUG
        //发送短信正文结束标识
#if (PLATFORM == LINUX)
        if (SIM900A_run("\x1B", 20000, NULL, NULL) == AT_RETURN_OK)
        /*if (SIM900A_run("\x1A", 20000, NULL, NULL) == AT_RETURN_OK)*/
#else 
        if (SIM900A_run("\x1A", 20000, NULL, NULL) == AT_RETURN_OK)
#endif
        /*if (SIM900A_run("\x1b", 20000, NULL, NULL) == AT_RETURN_OK)*/
        {
            proc_total_len += proc_len;
            continue;
        }
        else
        {
            return -1;
        }
#else
        proc_total_len += proc_len;
#endif
    }
    while(proc_total_len < msg_len && proc_len != 0);
    return proc_total_len;
}

